/* eslint  react/no-string-refs: 0 */
import React, { Component } from 'react';
import axios from 'axios'
import IceContainer from '@icedesign/container';
import { Input, Grid, Form, Select, Tree, Button, Message } from '@alifd/next';
import {observer, inject} from 'mobx-react';
import moment from 'moment';
import { ApiOfResourceGet, ApiOfControlTest, ApiOfRealtimeMonitorData, ApiOfCollectTest } from '#/utils/coreApiDefine'
import { ApiOfEventList } from '#/utils/policyApiDefine'

const { Row, Col } = Grid;
const Option = Select.Option;
const d3 = require("d3");

const images = {
  '_0053': require('./images/0053.png'),
  'default': require('./images/default.png'),
}

@inject('dictstore')
@observer
export default class Monitor extends Component {
  static displayName = 'Monitor';

  static propTypes = {};

  static defaultProps = {};

  constructor(props) {
    super(props);
    this._isMounted = false;
    this.state = {
      d3Data: {
        name: '',
        children: [],
      },
      editInstance: {},
      collectedTime: '',
      refreshTimeout: 180,
      metrics: [],
      visiableMetrics: [],
      forceRedraw: false,
      fullScreen: false,
    };
    this.config = {
      alarms: [],
    }
  }

  componentDidMount = () => {
    let that = this;
    this._isMounted = true;
    axios.all([ApiOfResourceGet(this.props.match.params.code), ApiOfRealtimeMonitorData(this.props.match.params.code)])
    .then(axios.spread( (response1, response2) => {
      let editInstance = response1.data
      let monitorData = response2.data;
      let formattedData = that.formatData(editInstance, monitorData)
      if(formattedData) {
        that.setState({
          editInstance: editInstance,
          d3Data: formattedData.d3Data,
          collectedTime: formattedData.collectedTime,
          // metrics: formattedData.metrics,
          // visiableMetrics: formattedData.visiableMetrics,
        });
      } else {
        that.setState({
          editInstance: editInstance,
        });
      }
      that.fetch();
      that.initSvg();
      that.updateMonitorDisplay();
    }));
  }

  formatData = (resource, data) => {
    let that = this;

    if(!data.hasOwnProperty('data') || !data.data) return;
    let metrics = data.data.map((v,i)=>{ return {label: v.name, key: v.code} });
    let visiableMetrics = that.state.visiableMetrics.length > 0 ? that.state.visiableMetrics : data.data.slice(0, 10).map((v,i)=>{return v.code;});
    let displayData = data.data.filter(v=>visiableMetrics.findIndex(value => value == v.code) != -1).map((v,i)=>{v.value0=1;v.type='collect'; return v;});
    let controlGroups = resource && resource.metricGroups ? resource.metricGroups.filter(it=>'CONTROL' == it.usageType) : [];
    if(controlGroups.length > 0) {
      controlGroups[0].metrics.forEach( (v,i)=> {
        v.value0=1;
        v.type='control';
        displayData.push(v);
      } )
    }
    let d3Data = {
      'name': resource.code,
      'children': displayData,
    }

    return {
      d3Data: d3Data,
      collectedTime: data.collectedTime,
      metrics: metrics,
      visiableMetrics: visiableMetrics,
    }
  }

  componentDidUpdate = () => {
    this.updateMonitorDisplay();
  }

  componentWillUnmount = () => {
    this._isMounted = false;
    if(this.timeoutId !== undefined) clearTimeout(this.timeoutId);
  }

  fetch = () => {
    let that = this;
    let resourceCode = this.props.match.params.code;

    ApiOfCollectTest(resourceCode).then(response => {
      if(!that._isMounted) return;
      that.timeoutId = setTimeout(() => {
        that.fetch();
      }, that.state.refreshTimeout  * 1000);

      if(response.status != 'success') return;
      let data = {
        code: resourceCode,
        collectedTime: response.data.collectBeginTime,
        data: response.data.resourceBean[0].metrics,
      }
      let formattedData = that.formatData(that.state.editInstance, data)
      if(formattedData) {
        that.setState({
          d3Data: formattedData.d3Data,
          collectedTime: formattedData.collectedTime,
          metrics: formattedData.metrics,
          visiableMetrics: formattedData.visiableMetrics,
        });
      }
    })

    ApiOfEventList({s_resourceCode: this.props.match.params.code, s_status: 'UNFIXED'}).then(response => {
      let alarms = response.data
      alarms.forEach(it => {
        if(!it.metricCode) it.metricCode = '';
      })
      that.config.alarms = alarms;
    })
  }

  initSvg = () => {
    let svg = d3.select("#monitorSvg").append("g")
    svg.append("g").attr("class", "image");
    svg.append("g").attr("class", "panels");
    svg.append("g").attr("class", "lines");
    svg.append("g").attr("class", "startpoints");

    let width = 900,
        height = 600,
        radius = Math.min(width, height) / 2;

    let pie = d3.pie().value(function(d) {
      return d.value0;
    });
    let arc = d3.arc()
      .outerRadius(radius * 0.3)
      .innerRadius(radius * 0.1);
    let outerArc = d3.arc()
      .innerRadius(radius * 0.7)
      .outerRadius(radius * 0.9);
    let key = d => d.data.code;
    let midAngle = d => {
      return d.startAngle + (d.endAngle - d.startAngle)/2;
    }

    Object.assign(this.config, {
      svg: svg,
      width: width,
      height: height,
      radius: radius,
      pie: pie,
      arc: arc,
      outerArc: outerArc,
      midAngle: midAngle,
      key: key,
      rectWidth: 120,
      rectHeight: 40,
      normalColor: '#006600',
      controlColor: '#104E8B',
      alarmColor: 'red',
    })
  }

  updateMonitorDisplay = () => {
    let that = this;
    let forceRedraw = this.state.forceRedraw;
    let config = this.config;

    if(!config.svg) return;
    config.svg.attr("transform", "translate(" + config.width / 2 + "," + config.height / 2 + ")");
    if(forceRedraw) {
      config.svg.select(".panels").selectAll('.el').remove();
      config.svg.select(".lines").selectAll('polyline').remove();
      config.svg.select(".startpoints").selectAll('circle').remove();
    }

    draw_image(config);
    draw_startpoints(config);
    draw_rect(config);
    draw_rect_divider(config);
    draw_text(config);
    draw_polylines(config);

    function draw_image(config) {
      let update = config.svg.select('.image').selectAll('image')
        .data([that.state.editInstance]);

      update.enter()
        .append('image')
        .style("width", config.radius)
        .style("height",config.radius)
        .attr("transform", function(d) {
            return "translate("+ -config.radius/2 + ',' + -config.radius/2 +")";
        });

      update.attr("href",function(d){
          return images['_' + d.type] ? images['_' + d.type] : images['default']
        })
    }

    function draw_startpoints(config) {
      let update = config.svg.select('.startpoints')
        .selectAll('circle')
        .data(config.pie(that.state.d3Data.children), config.key);

      update.enter()
        .append('circle')
        .attr('cx', d=>config.arc.centroid(d)[0])
        .attr('cy', d=>config.arc.centroid(d)[1])
        .attr('r', 3)
        .style('fill', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1 ) == -1 ? config.normalColor:config.alarmColor)
        .exit()
        .remove();
      update.style('fill', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
      update.exit().remove()
    }

    function draw_polylines(config) {
      let update = config.svg.select(".lines").selectAll("polyline")
        .data(config.pie(that.state.d3Data.children), config.key);

      update.enter()
        .append("polyline")
        .style('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
        .style('stroke-width', '1')
        .style('fill', 'none')
        .attr('points', d => {
          let pos = config.outerArc.centroid(d);
          pos[0] = config.radius * 0.8 * (config.midAngle(d) < Math.PI ? 1 : -1);
          return [config.arc.centroid(d), config.outerArc.centroid(d), pos];
        });
      update.style('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
      update.exit().remove()
    }

    function draw_rect(config) {
      let update = config.svg.select(".panels").selectAll("rect")
        .data(config.pie(that.state.d3Data.children), config.key);
      update.enter()
        .append("rect")
        .attr('class', 'el')
        .attr('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
        .attr('fill','#ffffff')
        .attr('width',config.rectWidth)
        .attr('height',config.rectHeight)
        .attr('rx',5)
        .attr('ry',5)
        .attr("transform", function(d) {
          let pos = config.outerArc.centroid(d);
          pos[0] = config.radius * 0.8 * (config.midAngle(d) < Math.PI ? 1 : -1) + (config.midAngle(d) < Math.PI ? 0 : -config.rectWidth);
          pos[1] = pos[1] - config.rectHeight/2
          return "translate("+ pos +")";
        })
      update.attr('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
      update.exit().remove();
    }

    function draw_rect_divider(config) {
      let line = d3.line()
        .x(d=> d[0])
        .y(d=> d[1]);

      let update = config.svg.select(".panels").selectAll("path")
        .data(config.pie(that.state.d3Data.children), config.key);

      update.enter()
        .append("path")
        .attr('class', 'el')
        .attr('d', d => line([[0,config.rectHeight/2],[config.rectWidth,config.rectHeight/2]]))
        .attr('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
        .attr('stroke-width', '1')
        .attr("transform", function(d) {
          let pos = config.outerArc.centroid(d);
          pos[0] = config.radius * 0.8 * (config.midAngle(d) < Math.PI ? 1 : -1) + (config.midAngle(d) < Math.PI ? 0 : -config.rectWidth);
          pos[1] = pos[1] - config.rectHeight/2
          return "translate("+ pos +")";
        });
      update.attr('stroke', d => config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor)
      update.exit().remove();
    }

    function draw_text(config) {
      let title_update = config.svg.select(".panels").selectAll("text.title")
        .data(config.pie(that.state.d3Data.children), config.key);
      title_update.exit().remove();
      title_update.enter()
        .append("text")
        .attr('class', 'el title')
        .text(d =>d.data.name)
        .attr('x', d=>{
          let pos = config.outerArc.centroid(d);
          pos[0] = config.radius * 0.8 * (config.midAngle(d) < Math.PI ? 1 : -1);
          return pos[0] + (config.midAngle(d) < Math.PI ? config.rectWidth /2 : -config.rectWidth/2);
        })
        .attr('y', d=>{
          let pos = config.outerArc.centroid(d);
          pos[1] = pos[1] - 5
          return pos[1];
        })
        .style('text-anchor', "middle");

      let content_update = config.svg.select(".panels").selectAll("text.content")
        .data(config.pie(that.state.d3Data.children), config.key);
      content_update.exit().remove();

      let enter = content_update.enter()
        .append("text")
          .attr('class', 'el content')
          .attr("font-size", "1.2em")
          .attr('font-weight', 'bold')
          .attr('fill', d => d.data.type=='collect' ? (config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor):config.controlColor)
          .attr('x', d=>{
            let pos = config.outerArc.centroid(d);
            pos[0] = config.radius * 0.8 * (config.midAngle(d) < Math.PI ? 1 : -1);
            return pos[0] + (config.midAngle(d) < Math.PI ? config.rectWidth /2 : -config.rectWidth/2);
          })
          .attr('y', d=>{
            let pos = config.outerArc.centroid(d);
            pos[1] = pos[1] + 15
            return pos[1];
          })
          .style('text-anchor', "middle")
          .text(d => {
            // let alarm = config.alarms.find(a=>a.metricCode == d.data.code)
            // return alarm ? alarm.content : d.data.value
            return d.data.type == 'collect' ? d.data.value : '点击执行'
          })
          .on("click",function(d,i){
            if(d.data.type == 'collect') return;
            ApiOfControlTest(that.props.match.params.code, d.data.code).then(response => {
              Message.show({
                  type: response.data ? 'success': 'error',
                  content: response.data ? '控制成功' : '控制失败',
                  duration: 2000
              });
            })
          })
          .style('cursor', (d,i)=>{
            return d.data.type == 'control' ? 'pointer' : 'default'
          })

      content_update.text(d => {
        // let alarm = config.alarms.find(a=>a.metricCode == d.data.code)
        // return alarm ? alarm.content : d.data.value
        return d.data.type == 'collect' ? d.data.value : '点击执行'
      })
      .attr('fill', d => d.data.type=='collect' ? (config.alarms.findIndex(a=>a.metricCode.indexOf(d.data.code)>-1) == -1 ? config.normalColor:config.alarmColor):config.controlColor)
    }
  }

  renderGateway = () => {
    if(this.state.editInstance.gateway) {
      let gateway = this.state.editInstance.gateway;
      return (
        <dl style={styles.horizontal.dl}>
          <dt style={styles.horizontal.dt}>网关名称</dt>
          <dd style={styles.horizontal.dd}>{ gateway.name }</dd>
        </dl>
      )
    } else {
       return (<dl></dl>)
    }
  }

  renderMetricGroup = () => {
    let metricGroups = this.state.editInstance.metricGroups;
    if(metricGroups && metricGroups.length > 0) {
      return (
        <dl style={styles.horizontal.dl}>
          <dt style={styles.horizontal.dt}>协议名称</dt>
          <dd style={styles.horizontal.dd}>{ metricGroups[0].protocol }</dd>
        </dl>
      )
    } else {
       return (<dl></dl>)
    }
  }

  onChangeRefreshFrequency = (refreshTimeout) => {
    this.setState({
      refreshTimeout
    })

    this.fetch();
  }

  onTreeCheck(keys, info) {
    let displayData = this.state.d3Data.children.filter(v=>keys.findIndex(key => key == v.code) != -1).map((v,i)=>{v.value0=1; return v;});
    this.setState({
        forceRedraw: true,
        visiableMetrics: keys,
        d3Data: {
          children: displayData
        }
    });
  }

  onFullScreen = ()=> {
    let isFullScreen = !this.state.fullScreen
    let offsetWidth = document.querySelector('body').offsetWidth
    let offsetHeight = document.querySelector('body').offsetHeight
    this.config.width = isFullScreen ? offsetWidth : '900'
    this.config.height = isFullScreen ? offsetHeight : '600'
    this.setState({
      forceRedraw: true,
      fullScreen: isFullScreen
    })
  }

  onExecuteControlMetric = (metric) => {
    ApiOfControlTest(this.props.match.params.code, metric).then(response => {
      Message.show({
          type: response.data ? 'success': 'error',
          content: response.data ? '控制成功' : '控制失败',
          duration: 2000
      });
    })
  }


  render() {
    let resource = this.state.editInstance;
    let collectedTime = !!this.state.collectedTime ? moment(this.state.collectedTime).format('YYYY-MM-DD HH:mm:ss') : '无';
    return (
      <div className="monitor-display">
        <IceContainer>
          <Row>
            <Col l="18">
              <div style={this.state.fullScreen ? styles.fullScreen : styles.nonFullScreen}>
                <div style={this.state.fullScreen ? {width: '100%', height: '30px', textAlign: 'center', lineHeight: '30px', fontSize: '16px'}: {width: '900px', height: '30px', textAlign: 'center', lineHeight: '30px', fontSize: '16px'}}>
                  { resource.name }({ resource.code }) { collectedTime }
                  <Button onClick={this.onFullScreen} type="primary" text style={{float: 'right', 'marginRight': '10px'}}>[全屏]</Button>
                </div>
                <svg id="monitorSvg" style={this.state.fullScreen?{width: '100%', height: '100%'}:{width: '900px', height: '600px'}}></svg>
              </div>
            </Col>
            <Col l="6">
              <div style={{width: "100%", height: '630px', border: '1px solid #c0c0c0', borderRadius: '10px', padding: '10px'}}>
                <dl style={styles.horizontal.dl}>
                  <dt style={styles.horizontal.dt}>刷新频率</dt>
                  <dd style={styles.horizontal.dd}>
                    <Select id="refresh-frequency" onChange={this.onChangeRefreshFrequency} defaultValue="180" size="small" style={{width: '100%'}}>
                        <Option value="1">1 秒</Option>
                        <Option value="3">3 秒</Option>
                        <Option value="10">10 秒</Option>
                        <Option value="30">30 秒</Option>
                        <Option value="60">1 分钟</Option>
                        <Option value="180">3 分钟</Option>
                    </Select>
                  </dd>
                </dl>
                <dl style={styles.horizontal.dl}>
                  <dt style={styles.horizontal.dt}>显示项目</dt>
                  <dd style={styles.horizontal.dd}>
                    <div style={{overflow: 'auto', height: '580px'}}>
                      <Tree checkable
                        checkedKeys={this.state.visiableMetrics}
                        onCheck={this.onTreeCheck.bind(this)}
                        dataSource={this.state.metrics} />
                    </div>
                  </dd>
                </dl>
              </div>
            </Col>
          </Row>
        </IceContainer>
      </div>
    );
  }
}

const styles = {
  label: {
    textAlign: 'right',
  },
  formContent: {
    width: '100%',
    position: 'relative',
  },
  formItem: {
    alignItems: 'center',
    marginBottom: 25,
  },
  formTitle: {
    margin: '0 0 20px',
    paddingBottom: '10px',
    borderBottom: '1px solid #eee',
  },
  horizontal: {
    dl: {
      margin: '0',
      lineHeight: '30px',
    },
    dt: {
      float: 'left',
      width: '80px',
      overflow: 'hidden',
      clear: 'left',
      textAlign: 'right',
      textOverflow: 'ellipsis',
      whiteSpace: 'nowrap',
    },
    dd: {
      marginLeft: '100px',
    }
  },
  fullScreen: {
    position: 'fixed',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    border: '1px solid #c0c0c0', 
    borderRadius: '0',
    zIndex: 1,
    background: '#ffffff',
  },
  nonFullScreen: {
    width: '900px', 
    height: '630px', 
    border: '1px solid #c0c0c0', 
    borderRadius: '10px'
  }
};
